package com.gitee.cliveyuan.tools;

import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 字符串工具
 *
 * @author clive
 * Created on 2018/07/23
 * @since 1.0
 */
public class StringTools extends StringUtils {

    private static final Logger logger = LoggerFactory.getLogger(StringTools.class);

    private StringTools() {
    }

    public static boolean hasLength(String str) {
        return (str != null && str.length() > 0);
    }

    /**
     * 获取字符串的unicode编码
     *
     * @param str 字符串
     * @return unicode
     */
    public static String stringToUnicode(String str) {
        if (Objects.isNull(str)) return null;
        char[] chars = str.toCharArray();
        StringBuilder returnStr = new StringBuilder();
        for (char aChar : chars) {
            returnStr.append("\\u")
                    .append(Integer.toString(aChar, 16));
        }
        return returnStr.toString();
    }

    /**
     * Unicode转汉字字符串
     *
     * @param str unicode
     * @return 字符串
     */
    public static String unicodeToString(String str) {

        Pattern pattern = Pattern.compile("(\\\\u(\\p{XDigit}{4}))");
        Matcher matcher = pattern.matcher(str);
        char ch;
        while (matcher.find()) {
            //group 6728
            String group = matcher.group(2);
            //ch:'木' 26408
            ch = (char) Integer.parseInt(group, 16);
            //group1 \u6728
            String group1 = matcher.group(1);
            str = str.replace(group1, ch + "");
        }
        return str;
    }


    /**
     * Check whether the given CharSequence has actual text.
     * More specifically, returns {@code true} if the string not {@code null},
     * its length is greater than 0, and it contains at least one non-whitespace character.
     * <p><pre class="code">
     * StringUtils.hasText(null) = false
     * StringUtils.hasText("") = false
     * StringUtils.hasText(" ") = false
     * StringUtils.hasText("12345") = true
     * StringUtils.hasText(" 12345 ") = true
     * </pre>
     *
     * @param str the CharSequence to check (may be {@code null})
     * @return {@code true} if the CharSequence is not {@code null},
     * its length is greater than 0, and it does not contain whitespace only
     * @see Character#isWhitespace
     */
    public static boolean hasText(String str) {
        if (!hasLength(str)) {
            return false;
        }
        int strLen = str.length();
        for (int i = 0; i < strLen; i++) {
            if (!Character.isWhitespace(str.charAt(i))) {
                return true;
            }
        }
        return false;
    }

    /**
     * map转查询字符串
     * 如: a=1&b=2&c=3&...
     * @param data
     */
    public static String map2QueryStr(Map<String, String> data) {
        if (CollectionTools.isEmpty(data)) return null;
        StringBuilder sb = new StringBuilder();
        data.forEach((k, v) ->
                sb.append(k)
                        .append("=")
                        .append(v)
                        .append("&")
        );
        if (sb.length() > 0) {
            sb.deleteCharAt(sb.length() - 1);
        }
        return sb.toString();
    }

    public static Map<String, String> queryStr2Map(String query) {
        Map<String, String> map = Maps.newHashMap();
        if (isBlank(query)) return map;
        String[] sections = query.split("&");
        for (String section : sections) {
            String[] pairs = section.split("=");
            if (pairs.length >= 2) {
                map.put(pairs[0], pairs[1]);
            } else {
                map.put(pairs[0], "");
            }
        }
        return map;
    }

    /**
     * map转cookie字符串
     * 如: a=1;b=2;c=3;...
     * @param data
     */
    public static String map2CookieStr(Map<String, String> data) {
        if (CollectionTools.isEmpty(data)) return null;
        StringBuilder sb = new StringBuilder();
        data.forEach((k, v) ->
                sb.append(k)
                        .append("=")
                        .append(v)
                        .append(";")
        );
        return sb.toString();
    }

    /**
     * 超出截断
     *
     * @param str       字符串
     * @param maxLength 最大长度
     */
    public static String substringIfOverflow(String str, int maxLength) {
        if (ValidateTools.maxLength(maxLength, str))
            return str;
        return str.substring(0, maxLength);
    }

    /**
     * str to map
     * k1:v1\n
     * k2:v2\n
     *
     * @param str
     */
    public static Map<String, String> str2Map(String str) {
        Map<String, String> map = Maps.newHashMap();
        if (Objects.isNull(str)) return map;
        String[] lines = str.split("\n");
        for (String line : lines) {
            if (StringTools.isBlank(line)) continue;
            int index = line.indexOf(":");
            if (index < 0) {
                map.put(line.trim(), StringTools.EMPTY);
                continue;
            }
            map.put(line.substring(0, index).trim(), line.substring(index + 1, line.length()).trim());
        }
        return map;
    }

    /**
     * URL编码
     *
     * @param text
     */
    public static String urlEncode(String text) {
        try {
            return URLEncoder.encode(text, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            return null;
        }
    }

    /**
     * URL解码
     *
     * @param text
     */
    public static String urlDecode(String text) {
        try {
            return URLDecoder.decode(text, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            return null;
        }
    }

    /**
     * Returns an array with strings between start and end.
     *
     * @param value input
     * @param start start
     * @param end   end
     * @return Array containing different parts between start and end.
     */

    public static String[] between(final String value, final String start, final String end) {
        Assert.notNull(value, "'value' should be not null.");
        Assert.notNull(start, "'start' should be not null.");
        Assert.notNull(end, "'end' should be not null.");
        String[] parts = value.split(end);
        return Arrays.stream(parts)
                .filter(subPart -> subPart.contains(start))
                .map(subPart -> subPart.substring(subPart.indexOf(start) + start.length()))
                .toArray(String[]::new);
    }

    /**
     * Returns first matched string between start and end.
     *
     * @param value input
     * @param start start
     * @param end   end
     */
    public static String firstBetween(final String value, final String start, final String end) {
        String[] betweenArray = between(value, start, end);
        if (CollectionTools.isNotEmpty(betweenArray)) return betweenArray[0];
        return EMPTY;
    }
}
